package org.summerframework.core.jackson;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.BeanProperty;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.ContextualSerializer;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import java.io.IOException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/**
 * @author 石超
 * @version v1.0.0
 */
@Slf4j
public class JsonJoinListSerializer extends JsonSerializer<List> implements ContextualSerializer {
    private JsonJoinList jsonJoinList;
    private final Map<String, Method> methods = new LinkedHashMap<>();
    private static final JsonJoinListNullSerializer JSON_JOIN_LIST_NULL_SERIALIZER = new JsonJoinListNullSerializer();

    public JsonJoinListSerializer() {
    }

    public JsonJoinListSerializer(JsonJoinList jsonJoinList) {
        this.jsonJoinList = jsonJoinList;
    }

    @Override
    public void serialize(List value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
        if (jsonJoinList != null) {
            String key = gen.getOutputContext().getCurrentName();
            if (StringUtils.isNotBlank(jsonJoinList.target())) {
                if (methods.isEmpty()) {
                    methods.put(key, getClass(gen.getOutputContext().getCurrentValue().getClass(), jsonJoinList.target()));
                }

                try {
                    Object invoke = methods.get(key).invoke(gen.getOutputContext().getCurrentValue());
                    if (invoke instanceof List) {
                        List list = new ArrayList();


                        for (Object v : (List) invoke) {
                            if (!methods.containsKey("get")) {
                                methods.put("get", getClass(v.getClass(), jsonJoinList.fieldName()));
                            }

                            if (methods.get(key) != null) {
                                list.add(methods.get("get").invoke(v));
                            }
                        }

                        gen.writeString(StringUtils.join(list, jsonJoinList.separator()));

                    } else {
                        gen.writeString("");
                    }
                } catch (Exception e) {
                    gen.writeString("");
                }
            } else {
                gen.writeString(StringUtils.join(value, ","));
            }
        } else {
            gen.writeObject(value);
        }
    }

    private String firstCharacterUppercase(String str) {
        return str.substring(0, 1).toUpperCase() + str.substring(1);
    }

    @Override
    public JsonSerializer<?> createContextual(SerializerProvider serializerProvider, BeanProperty beanProperty) throws
            JsonMappingException {
        if (beanProperty != null) { // 为空直接跳过
            JsonJoinList annotation = beanProperty.getAnnotation(JsonJoinList.class);
            if (annotation == null) {
                annotation = beanProperty.getContextAnnotation(JsonJoinList.class);
            }
            if (annotation != null) { // 如果能得到注解，就将注解的 value 传入 ImageURLSerialize
                JsonJoinListSerializer jsonJoinListSerializer = new JsonJoinListSerializer(annotation);
                serializerProvider.setNullValueSerializer(JsonJoinListNullSerializer.addKey(beanProperty.getMember().getDeclaringClass(), beanProperty.getName(), jsonJoinListSerializer));
                return jsonJoinListSerializer;
            }

            return serializerProvider.findValueSerializer(beanProperty.getType(), beanProperty);
        }

        return serializerProvider.findNullValueSerializer(beanProperty);
    }

    private static Method getClass(Class classs, String key) {
        StringBuffer sb = new StringBuffer();
        sb.append("get");
        sb.append(key.substring(0, 1).toUpperCase());
        sb.append(key.substring(1));

        try {
            Method declaredMethod = classs.getDeclaredMethod(sb.toString());
            declaredMethod.setAccessible(true);
            return declaredMethod;
        } catch (NoSuchMethodException e) {
            if (classs.getSuperclass() != null) {
                return getClass(classs.getSuperclass(), key);
            }
        }

        return null;
    }
}
